﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using MentalAlchemy.Atomics;

namespace Battles.Atomics
{
	[Serializable]
	public class Types
	{
		public string[] ArmyPresets = {"Strong", "Weak", "Random"};
	}

	[Serializable]
	public enum WarriorType
	{
		Archer,
		Cavalry,
		HeavyInfantry,
		LightInfantry
	} ;

	/// <summary>
	/// When creating a warrior (MAX_SKILL - MIN_SKILL) * N / 2 skill points should be
	/// distributed, where N -- number of battle skills.
	/// </summary>
	[Serializable]
	public class Warrior
	{
		public const float DEFAULT_AGE = 16;
		public const float DEFAULT_HEALTH = 100;
		public const float DEFAULT_EXPERIENCE = 0;
		public const string DEFAULT_NAME = "Nils";
		public const float MIN_SKILL = 0;
		public const float MAX_SKILL = 10;


		#region - General traits. -
		public string Name { get; set; }
		public float Age { get; set; }
		public float Health { get; set; }
		public float Experience { get; set; }
		public WarriorType Type { get; set; }
		#endregion

		#region - Battle skills. -
		public float Attack { get; set; }
		public float Defence { get; set; }
		public float Speed { get; set; } 
		#endregion

		public Warrior ()
		{
			Age = DEFAULT_AGE;
			Name = DEFAULT_NAME;
			Experience = DEFAULT_EXPERIENCE;

			Health = DEFAULT_HEALTH;
		}
	}

	[Serializable]
	public enum ArmyPreset
	{
		Random,
		Light,
		Heavy
	} ;

	/// <summary>
	/// Class for army.
	/// </summary>
	[Serializable]
	public class Army
	{
		#region - Properties. -
		public List<Warrior> Archers { get; set; }
		public List<Warrior> LightInf { get; set; }
		public List<Warrior> HeavyInf { get; set; }
		public List<Warrior> Cavalry { get; set; } 
		public List<Warrior> Warriors
		{
			get
			{
				var res = new List<Warrior>();
				res.AddRange(Archers);
				res.AddRange(Cavalry);
				res.AddRange(HeavyInf);
				res.AddRange(LightInf);
				return res;
			}
		}

		/// <summary>
		/// Returns total strength of the army.
		/// Calculated as: A + D + S * 1.5,
		/// where A -- total attack, D -- total defence and S -- total speed of the army.
		/// </summary>
		public float Strength
		{
			get
			{
				var res = 0f;
				foreach (var archer in Archers)
				{
					res += archer.Attack + archer.Defence + archer.Speed*1.5f;
				}
				foreach (var cav in Cavalry)
				{
					res += cav.Attack + cav.Defence + cav.Speed * 1.5f;
				}
				foreach (var archer in Archers)
				{
					res += archer.Attack + archer.Defence + archer.Speed * 0.5f;
				}
				foreach (var archer in Archers)
				{
					res += archer.Attack + archer.Defence + archer.Speed * 0.5f;
				}
				foreach (var archer in Archers)
				{
					res += archer.Attack + archer.Defence + archer.Speed * 0.5f;
				}

				return res;
			}
		}
		#endregion

		public Army ()
		{
			Archers = new List<Warrior>();
			LightInf = new List<Warrior>();
			HeavyInf = new List<Warrior>();
			Cavalry = new List<Warrior>();
		}

		public static Army Create (int size, ArmyPreset preset)
		{
			var army = new Army();

			// todo: add support for army presets. Meanwhile use only what corresponds to random preset.
			for (int i = 0; i < size; i++)
			{
				// get random warrior type.
				var warrior = Utils.CreateRndWarrior();
				if (warrior.Type == WarriorType.Archer)
				{
					army.Archers.Add(warrior);
				}
				else if (warrior.Type == WarriorType.Cavalry)
				{
					army.Cavalry.Add(warrior);
				}
				else if (warrior.Type == WarriorType.HeavyInfantry)
				{
					army.HeavyInf.Add(warrior);
				}
				else if (warrior.Type == WarriorType.LightInfantry)
				{
					army.LightInf.Add(warrior);
				}
			}

			return army;
		}

		public void Remove (Warrior w)
		{
			if (w.Type == WarriorType.Archer)
			{
				Archers.Remove(w);
			}
			else if (w.Type == WarriorType.Cavalry)
			{
				Cavalry.Remove(w);
			}
			else if (w.Type == WarriorType.HeavyInfantry)
			{
				HeavyInf.Remove(w);
			}
			else if (w.Type == WarriorType.LightInfantry)
			{
				LightInf.Remove(w);
			}
		}
	}

	/// <summary>
	/// Class to define army placement. To be used by battle engine to simulate battle.
	/// </summary>
	[Serializable]
	public class ArmyPlacement
	{
		/// <summary>
		/// The first row of warriors.
		/// </summary>
		public List<Warrior> Row1 { get; set; }
		
		/// <summary>
		/// The second row of warriors.
		/// </summary>
		public List<Warrior> Row2 { get; set; }

		public ArmyPlacement (int rowSize)
		{
			Row1 = new List<Warrior>(rowSize);
			Row2 = new List<Warrior>(rowSize);
		}
	}

	/// <summary>
	/// Class to store battle results.
	/// </summary>
	[Serializable]
	public class BattleResult
	{
		public float Army1Casualties { get; set; }
		public float Army2Casualties { get; set; }
		public bool Army1Wins { get; set; }
	}

	[Serializable]
	public class Utils
	{
		#region - Creation. -
		/// <summary>
		/// Creates random warrior.
		/// </summary>
		/// <returns></returns>
		public static Warrior CreateRndWarrior()
		{
			var names = Enum.GetNames(typeof(WarriorType));
			var typesCount = names.Length;
			var typeName = names[ContextRandom.Next(typesCount)];
			var type = (WarriorType)Enum.Parse(typeof(WarriorType), typeName);
			return CreateWarrior(type);
		}

		/// <summary>
		/// </summary>
		/// <param name="type"></param>
		/// <returns></returns>
		public static Warrior CreateWarrior(WarriorType type)
		{
			var warrior = new Warrior();
			warrior.Type = type;
			switch (type)
			{
				case WarriorType.Archer:
					warrior.Attack = 5;
					warrior.Defence = 3;
					warrior.Speed = 7;	// 10.25
					break;
				case WarriorType.Cavalry:
					warrior.Attack = 3;
					warrior.Defence = 2;
					warrior.Speed = 10;	// 7.75
					break;
				case WarriorType.HeavyInfantry:
					warrior.Attack = 6;
					warrior.Defence = 6;
					warrior.Speed = 3;	// 10.39
					break;
				case WarriorType.LightInfantry:
					warrior.Attack = 5;
					warrior.Defence = 5;
					warrior.Speed = 5;	// 11.18
					break;
				default:
					throw new Exception("Unknown warrior type (" + type + ")");
			}

			return warrior;
		} 
		#endregion

		#region - Search. -
		/// <summary>
		/// Returns warrior by his name.
		/// </summary>
		/// <param name="ws"></param>
		/// <param name="name"></param>
		/// <returns></returns>
		public static Warrior GetWarriorByName (List<Warrior> ws, string name)
		{
			foreach (var warrior in ws)
			{
				if (string.Compare(warrior.Name, name, true) == 0)
				{
					return warrior;
				}
			}
			return null;
		}
		#endregion

		#region - Army stats. -
		public static float GetTotalHealth(List<Warrior> army)
		{
			var res = 0f;
			foreach (var warrior in army)
			{
				res += warrior.Health;
			}
			return res;
		}
		
		public static List<string> GetAllNames(List<Warrior> army)
		{
			var res = new List<string> ();
			foreach (var warrior in army)
			{
				res.Add(warrior.Name);
			}
			return res;
		}
		#endregion

		#region - Misc. -
		public static void AssignRandomNames (List<Warrior> army, string[] names, string[] surnames)
		{
			foreach (var warrior in army)
			{
				AssignRandomName(warrior, names, surnames);
			}
		}

		public static void AssignRandomName(Warrior warrior, string[] names, string[] surnames)
		{
			var namesCount = names.Length;
			var surnamesCount = surnames.Length;
			var idx1 = ContextRandom.Next(namesCount);
			var idx2 = ContextRandom.Next(surnamesCount);
			warrior.Name = names[idx1] + " " + surnames[idx2];
		}
		#endregion
	}
}
